﻿#define CMD_FAST //定义快速的命令对象等方案，用于解决大批量快速更新的问题
//#define CMD_NORMAR //普通模式
using System;
using System.Linq;
using System.Xml;
using System.Collections.Generic;
using System.Data;
using System.Collections;

namespace Flagwind.Data
{
	/// <summary>
	/// 匿名实体操作类，在只知道实体类类型，但没有直接的实体类实例的情况下使用。
	/// </summary>
	public static class AnonymousAccess
	{
		#region 私有字段

		private static IDataProvider _dataProvider;

		#endregion

		#region 私有属性

		private static IDataProvider DataProvider
		{
			get
			{
				if(_dataProvider == null)
				{
					return DataProviderFactory.DefaultProvider;
				}
				else
				{
					return _dataProvider;
				}
			}
			set
			{
				_dataProvider = value;
			}
		}

		private static IDataProvider ExecuteProvider
		{
			get
			{
				if(_dataProvider == null)
				{
					return DataProviderFactory.GetProvider();
				}
				else
				{
					return _dataProvider;
				}
			}
		}

		#endregion

		#region 导入数据

		public static int Import(IEnumerable<IEntity> entities, int model)
		{
			return Import(entities, model, ExecuteProvider);
		}

		public static int Import(IEnumerable<IEntity> entities, int model, IDataProvider provider)
		{
			int count = 0;
			if(entities == null || !entities.Any())
			{
				return 0;
			}
			var list = entities.ToList();
#if(CMD_FAST)
			//如果是SQLSERVER，考虑批量复制的方式
			if(model > 0 && provider.ProviderType == DataProviderType.SqlServer)
			{
				if(model == 1)
				{
					//将目标数据库中对应的数据删除
					provider.BeginTransaction();
					try
					{
						count = DeleteData(list, provider);
						provider.Commit();
					}
					catch(Exception ex)
					{
						provider.Rollback();
						throw ex;
					}
				}
				//执行大批量复制
				DataTable source = list.ToDataTable<IEntity>();
				Utility.BatchCopySql(source, provider.ConnectionString, source.TableName, 500);
				return list.Count;
			}
			else
			{
				provider.BeginTransaction();
				try
				{
					count = ImportData(list, provider);
					provider.Commit();
				}
				catch(Exception ex)
				{
					provider.Rollback();
					throw ex;
				}
			}
#else
            provider.BeginTransaction();
            try
            {
                count = ImportData(list, provider);
                provider.Commit();
            }
            catch (Exception ex)
            {
                provider.Rollback();
                throw ex;
            }
#endif
			return count;
		}

		#endregion

		#region 保存数据

		public static int Save(IEnumerable<IEntity> entities)
		{
			return Save(entities, ExecuteProvider);
		}

		public static int Save(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			int count = 0;
			provider.BeginTransaction();
			try
			{
				count = InsertOrUpdate(entities, provider);
				provider.Commit();
			}
			catch(Exception ex)
			{
				provider.Rollback();
				throw ex;
			}
			return count;
		}

		#endregion

		#region 查询数据

		public static IEnumerable Select(Command command, Type entityType)
		{
			return Select(command, entityType, DataProvider);
		}

		public static IEnumerable Select(Command command, Type entityType, IDataProvider provider)
		{
			IDataReader reader = GetReader(command, entityType, provider);
			return Select(reader, entityType);
		}

		public static IEnumerable Select(IDataReader reader, Type entityType)
		{
			if(!entityType.IsSubclassOf(typeof(EntityBase)))
			{
				throw new Exception("当前类型不是 IEntity 的派生类型：" + entityType.FullName);
			}
			IList list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(entityType));
			using(reader)
			{
				if(reader.Read())
				{
					int fieldCount = reader.FieldCount;
					string[] names = new string[fieldCount];
					object[] values = null;
					for(int i = 0; i < fieldCount; i++)
					{
						names[i] = reader.GetName(i);
					}
					do
					{
						values = new object[fieldCount];
						reader.GetValues(values);
						IEntity entity = (IEntity)Activator.CreateInstance(entityType);
						entity.SetPropertyNames(names);
						entity.SetPropertyValues(values);
						list.Add(entity);
					} while(reader.Read());
				}
			}
			return list;
		}

		public static IEnumerable<T> Select<T>(IDataReader reader) where T : IDataReadable, new()
		{
			List<T> list = new List<T>();
			if(reader.Read())
			{
				int fieldCount = reader.FieldCount;
				string[] names = new string[fieldCount];
				for(int i = 0; i < fieldCount; i++)
				{
					names[i] = reader.GetName(i);
				}
				do
				{
					T t = new T();
					((IDataReadable)t).Read(reader, fieldCount, names);
					list.Add(t);
				} while(reader.Read());
			}
			return list;
		}

		public static IDataReader GetReader(Command command, Type entityType, IDataProvider provider)
		{
			return GetReader(command, entityType, provider, false);
		}

		public static IDataReader GetReader(Command command, Type entityType, IDataProvider provider, bool single)
		{
			var info = DataUtility.GetSqlInfo(command, entityType, provider, single);
			command.Dispose();
			return GetReader(info, provider, single);
		}

		public static IDataReader GetReader(SqlInfo info, IDataProvider provider, bool single)
		{
			IDataReader reader = null;
			if(info.Parameters != null && info.Parameters.Count > 0)
			{
				IDataParameter[] paras = DataUtility.GetParameters(info.Parameters, provider);
				if(single)
				{
					reader = provider.ExecuteDataReaderWithSingleRow(info.Sql, paras);
				}
				else
				{
					reader = provider.ExecuteDataReader(info.Sql, info.CommandType, paras);
				}
			}
			else
			{
				if(single)
				{
					reader = provider.ExecuteDataReaderWithSingleRow(info.Sql);
				}
				else
				{
					reader = provider.ExecuteDataReader(info.Sql, info.CommandType, null);
				}
			}
			return reader;
		}

		public static object Count(Command command, IDataProvider provider)
		{
			string sql = SqlPager.MakeSQLStringByPage(provider.ProviderType, command.ToString(), "", command.PageSize, command.PageIndex, 0);
			if(command.Parameters != null && command.Parameters.Count > 0)
			{
				IDataParameter[] paras = DataUtility.GetParameters(command.Parameters, provider);
				return provider.ExecuteScalar(sql, CommandType.Text, paras);
			}
			else
			{
				return provider.ExecuteScalar(sql);
			}
		}

		#endregion

		#region 执行方法

		public static object ExecuteScalar(Command command, IDataProvider provider)
		{
			if(command.Parameters != null && command.Parameters.Count > 0)
			{
				IDataParameter[] parameters = DataUtility.GetParameters(command.Parameters, provider);
				return provider.ExecuteScalar(command.ToString(), CommandType.Text, parameters);
			}
			else
			{
				return provider.ExecuteScalar(command.ToString());
			}
		}

		#endregion

		#region 其他方法

		public static string GetMapSql(Type entityType)
		{
			string typeFullName = entityType.FullName;
			string[] arrTemp = new string[2];
			int at = typeFullName.LastIndexOf('.');
			if(at > 0)
			{
				arrTemp[0] = typeFullName.Substring(0, at);
				arrTemp[1] = typeFullName.Substring(at + 1);
			}
			else
			{
				throw new Exception("EntityMapSql 实体类要求具有名称空间！");
			}
			string resourceName = "EntitySqlMap.config";
			string xmlConfig = null;

			//如果存在配置文件，则从文件读取
			if(System.IO.File.Exists(resourceName))
			{
				xmlConfig = System.IO.File.ReadAllText(resourceName);
			}
			else
			{
				xmlConfig = Utility.GetAssemblyResource(entityType, resourceName);
			}
			XmlDocument doc = new XmlDocument();
			doc.LoadXml(xmlConfig);
			XmlNode SqlText = default(XmlNode);
			XmlElement root = doc.DocumentElement;
			string objPath = "/configuration/Namespace[@name='" + arrTemp[0] + "']/Map[@name='" + arrTemp[1] + "']/Sql";
			SqlText = root.SelectSingleNode(objPath);
			if((SqlText != null) && SqlText.HasChildNodes)
			{
				return SqlText.InnerText;
			}
			return "";
		}

		#endregion

		#region 私有方法

		private static int DeleteData(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			//必须保证集合中的元素都是同一个类型
			if(entities == null || entities.Count() == 0)
			{
				return 0;
			}
			var entityList = entities.ToList();
			IEntity entity = entityList[0];
			if(entity.GetPrimaryKey().Count == 0)
			{
				throw new Exception("当前实体类未指定主键字段。");
			}
			int fieldCount = entity.GetPropertyNames().Length;
			if(fieldCount == 0)
			{
				throw new Exception("实体类属性字段为空。");
			}
			string tableName = entity.GetMappingName();
			for(int i = 1; i < entityList.Count; i++)
			{
				if(entityList[i].GetMappingName() != tableName)
				{
					throw new Exception("当前实体类集合的元素类型不一致，对应的表是：" + (entityList[i].GetMappingName()));
				}
			}
			//先将主键对应的记录删除，再插入

			#region 构造查询语句

			var command = new EntityCommand(entity, provider);
			//导入数据，不考虑自增列问题
			command.IdentityEnable = true;
			command.TargetFields = GetTargetFields(tableName, entity.GetPropertyNames(), provider).ToArray();
			string sql = command.DeleteCommand;
			IDataParameter[] parameters = command.DeleteParameters;

			#endregion

			int count = 0;
			foreach(IEntity item in entityList)
			{
				//执行删除
				foreach(IDataParameter parameter in parameters)
				{
					parameter.Value = item.GetPropertyValue(parameter.SourceColumn);
				}
				count += provider.ExecuteNonQuery(sql, CommandType.Text, parameters);
			}
			return count;
		}

		private static int InsertOrUpdate(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			//必须保证集合中的元素都是同一个类型
			if(entities == null || !entities.Any())
			{
				return 0;
			}
			var entityList = entities.ToList();
			IEntity entity = entityList[0];
			if(entity.GetPrimaryKey().Count == 0)
			{
				throw new Exception("当前实体类未指定主键字段。");
			}
			int fieldCount = entity.GetPropertyNames().Length;
			if(fieldCount == 0)
			{
				throw new Exception("实体类属性字段为空。");
			}
			var command = new EntityCommand(entity, provider);
			command.TargetFields = GetTargetFields(entity.GetMappingName(), entity.GetPropertyNames(), provider).ToArray();
			int allCount = 0;
			int updateCount = 0;
			int insertCount = 0;
			List<IDataParameter> parameters = new List<IDataParameter>();
#if ( !CMD_FAST)
			foreach(IEntity item in list)
            {
                parameters.Clear();
                foreach (string field in command.TargetFields)
                {
                    string paraName = "@" + field.Replace(" ", "");
					IDataParameter parameter = provider.GetParameter(paraName, item.GetPropertyValue(field));
                    parameters.Add(parameter);
                }
                
				//先做一部分修改，如果不成功就插入
				int count = provider.ExecuteNonQuery(command.UpdateCommand, CommandType.Text, parameters.ToArray());
                if (count <= 0)
					insertCount += provider.ExecuteNonQuery(command.InsertCommand, CommandType.Text, parameters.ToArray());
                else
                    updateCount += count;
            }
#else
			IDbConnection conn = provider.GetConnection();
			IDbCommand insertCmd = conn.CreateCommand();
			insertCmd.CommandText = command.InsertCommand;
			insertCmd.CommandType = CommandType.Text;
			if(command.InsertParameters != null)
			{
				foreach(IDataParameter para in command.InsertParameters)
				{
					insertCmd.Parameters.Add(para);
				}
			}
			IDbCommand updateCmd = conn.CreateCommand();
			updateCmd.CommandText = command.UpdateCommand;
			updateCmd.CommandType = CommandType.Text;
			if(command.UpdateParameters != null)
			{
				foreach(IDataParameter para in command.UpdateParameters)
				{
					updateCmd.Parameters.Add(para);
				}
			}
			foreach(IEntity item in entityList)
			{
				foreach(string field in command.TargetFields)
				{
					string paraName = provider.ParameterChar + field.Replace(" ", "");
					((IDataParameter)insertCmd.Parameters[paraName]).Value = item.GetPropertyValue(field);
					((IDataParameter)updateCmd.Parameters[paraName]).Value = item.GetPropertyValue(field);
				}
				//先做一部分修改，如果不成功就插入
				//直接使用Command对象的 ExecuteNonQuery ，加快处理速度
				int count = updateCmd.ExecuteNonQuery();
				if(count <= 0)
				{
					insertCount += insertCmd.ExecuteNonQuery();
				}
				else
				{
					updateCount += count;
				}
			}
#endif
			allCount = insertCount + updateCount * (entityList.Count + 1);
			return allCount;
		}

		/// <summary>
		/// 获取目标数据库表中的实际字段名称列表，目标库的字段可能跟实体类定义的字段数量不一样
		/// </summary>
		/// <param name="tableName"></param>
		/// <param name="PropertyNames"></param>
		/// <param name="DB"></param>
		/// <returns></returns>
		private static List<string> GetTargetFields(string tableName, string[] PropertyNames, IDataProvider provider)
		{
			//有可能目标库的字段数量跟实体类定义的不一致，需要先到目标库查询有哪些实际的字段
			DataSet dataSet = provider.ExecuteDataSetSchema("select * from [" + tableName + "]", CommandType.Text, null);
			List<string> targetFields = new List<string>();
			if(dataSet != null && dataSet.Tables.Count > 0)
			{
				foreach(DataColumn col in dataSet.Tables[0].Columns)
				{
					foreach(string field in PropertyNames)
					{
						if(string.Compare(col.ColumnName, field, true) == 0)
						{
							targetFields.Add(field);
							break;
						}
					}
				}
			}
			else
			{
				throw new Exception("获取目标表架构失败，表名称：" + tableName);
			}
			if(targetFields.Count == 0)
			{
				throw new Exception("获取目标表没有和当前实体类匹配的字段，表名称：" + tableName);
			}
			return targetFields;
		}

		/// <summary>
		/// 将实体集合中的所有数据导入数据库，如果数据已经存在则修改（先删除再插入）否则直接插入。如果实体中的数据只包含部分字段的数据，请勿使用该方法。
		/// </summary>
		/// <param name="entities">同一实体类集合</param>
		/// <param name="provider">数据访问对象实例</param>
		/// <returns>操作受影响的行数</returns>
		private static int ImportData(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			//必须保证集合中的元素都是同一个类型
			if(entities == null || !entities.Any())
			{
				return 0;
			}
			var entityList = entities.ToList();
			IEntity entity = entityList[0];
			if(entity.GetPrimaryKey().Count == 0)
			{
				throw new Exception("当前实体类未指定主键字段。");
			}
			int fieldCount = entity.GetPropertyNames().Length;
			if(fieldCount == 0)
			{
				throw new Exception("实体类属性字段为空。");
			}
			string tableName = entity.GetMappingName();
			for(int i = 1; i < entityList.Count; i++)
			{
				if(entityList[i].GetMappingName() != tableName)
				{
					throw new Exception("当前实体类集合的元素类型不一致，对应的表是：" + (entityList[i].GetMappingName()));
				}
			}
			//先将主键对应的记录删除，再插入

			#region 构造查询语句

			EntityCommand command = new EntityCommand(entity, provider);
			command.IdentityEnable = true; //导入数据，不考虑自增列问题
			command.TargetFields = GetTargetFields(tableName, entity.GetPropertyNames(), provider).ToArray();
			string delteSql = command.DeleteCommand;
			IDataParameter[] deleteParameters = command.DeleteParameters;
			string insertSql = command.InsertCommand;
			IDataParameter[] insertParameters = command.InsertParameters;

			#endregion

			int count = 0;
			foreach(IEntity item in entityList)
			{
				//执行删除
				foreach(IDataParameter parameter in deleteParameters)
				{
					parameter.Value = item.GetPropertyValue(parameter.SourceColumn);
				}
				count += provider.ExecuteNonQuery(delteSql, CommandType.Text, deleteParameters);
				//执行插入
				foreach(IDataParameter parameter in insertParameters)
				{
					parameter.Value = item.GetPropertyValue(parameter.SourceColumn);
				}
				count += provider.ExecuteNonQuery(insertSql, CommandType.Text, insertParameters);
			}
			return count;
		}

		#endregion
	}
}